﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

using JHStock.Update;
using Newtonsoft.Json;
using Tools;

namespace JHStock
{
    public delegate void ActionDeleGate(string action);
    public class StocksData
    {
        public StocksData(JHConfig Jhconfig, string kdatatype)
        {
            this.Jhconfig = Jhconfig;
            this._kdatatype = kdatatype;
            _urldayscount = Jhconfig.Jsconfig.GetUrlDaysCount(kdatatype);
            _netdate = new NetDate(_urldayscount, _kdatatype);
            _isrunning = false;
            _NeedDownLoadDays = 0;
            _tnkd = null;
            Msg = "";
        }   
        public string InitData() //必须等待完成
        {
            _netdate.Refresh();
            if (!_netdate.Inline)
            {
                if (LoadLocalData())
                    return "OK:断网状态，使用本地数据统计，本地数据最新日期为" + _localkdtags.StoreDate.ToShortDateString(); // +本地数据最新日期
                else
                    return "Quit:断网且无本地数据，无法工作，请退出";
            }
            else
            {  
                int mergersday = 0;
                if (NeedDownLoadAllData(ref mergersday)) // mergersday > 10 则重新下载全部
                {
                    DownLoadNetDataAndCheckSave(_urldayscount); //由 DaysLength
                    return "OK: 已下载\"完整数据\"并替换，当前数据日期为" + _localkdtags.StoreDate.ToShortDateString(); // +本地数据最新日期
                }
                else
                {
                    if (mergersday < 0)
                        return "OK:\"无需更新\"本地数据已经最新，当前数据日期为" + _localkdtags.StoreDate.ToShortDateString();
                    else
                    {
                        DownLoadNetDataAndCheckSave(mergersday);
                        return "OK: 已下载\"" + (mergersday -1) + "个周期\"数据 并合并至最新数据，，当前数据日期为" + _localkdtags.StoreDate.ToShortDateString(); // +本地数据最新日期
                    }
                }
            }
        }
        public void CompleteRun() //数据的处理合并
        {
            bool change = false;
            string Msg = ExCludeExchangeDayData();
            if (_NeedDownLoadDays == _urldayscount) //全部下载
            {
                _tnkd._NetObjTags.StoreDate = _netdate.LatestDateTime;
                _localkdtags = new LocalKdTagstocks(_tnkd._NetObjTags.StoreDate, _tnkd._NetObjTags.Tag.Length);
                _localkdtags.Init(_tnkd._NetObjTags);
                change = true;
            }
            else
            {
                StringBuilder sb = new StringBuilder(); // 合并   
                for (int i = 0; i < 2000; i++)
                {
                    NetObjectTagstock nt = _tnkd._NetObjTags.Tag[i];
                    LocalKdTagstock lt = _localkdtags.Tag[i];
                    if (lt != null && lt.kd != null)
                    {
                        List<KData> kdnt = (List<KData>)nt.ObjTag;
                        int Pos = lt.kd.IndexOf(nt.OldestKDate); //查找
                        if (Pos == -1)
                        {
                            bool containsdate = false;
                            foreach (KData kd in lt.kd)
                                if (kd.date == nt.OldestKDate.date)
                                {
                                    containsdate = true;
                                    break;
                                }
                            if (!containsdate)
                            {
                                if (nt.LatestKData.date == this._netdate.LatestDate)
                                {
                                    sb.AppendLine(nt.s.Code + "\tErrorContains:" + i + "_" + nt.s.Name + "逻辑错误，本应包含日期，却没有包含");
                                    lt.Valid = false;
                                    change = true;
                                }
                            }
                            else
                            {
                                lt.Valid = false;
                                change = true;
                            }
                        }
                        else
                        {
                            if (!IsEqual(lt.kd[Pos], nt.OldestKDate))
                            {
                                sb.AppendLine(nt.s.Code + "\tErrorEqual:" + i + "_" + nt.s.Name + " 逻辑错误，本应相等却不等");
                            }
                            lt.Valid = true;
                            lt.Latestkd = nt.LatestKData;
                            int skipcount = kdnt.Count + Pos -lt.kd.Count+1;
                            int takecount = lt.kd.Count - kdnt.Count;
                            lt.kd = lt.kd.Skip(skipcount).Take(takecount).ToList();
                            lt.kd.AddRange(kdnt);

                            if (lt.kd.Distinct().Count() != lt.kd.Count)
                            {
                                sb.AppendLine(nt.s.Code + "\tErrorExistDuplicate:" + i + "_" + nt.s.Name + " 存在重复数据");
                                lt.Valid = false; 
                            }
                            change = true;
                        }
                    }
                }
                if (sb.Length > 0)
                {
                    MFile.AppendAllText(this.Jhconfig.Jsconfig.pathconfig.NowWorkPath()+KdataType + "_MeregeData_Error.log", sb.ToString());
                }
            }// 理论上不存在
            if (change)
            {
                int findate = _localkdtags.StoreIntDate();
                _localkdtags.StoreFinDate = _localkdtags.StoreFinDate < findate ? _localkdtags.StoreFinDate : findate;
                _localkdtags.StoreDate = _netdate.LatestDateTime;
                _localkdtags.Save(locatepricedatafile);
            }
            else
            {
                ThreadShowMsg("没有任何更新");
            }
            
            _isrunning = false;
        }
        private string ExCludeExchangeDayData()
        {
            foreach (NetObjectTagstock nti in _tnkd._NetObjTags.Tag)
            {
                if (nti != null && nti.ObjTag != null)
                {
                    List<KData> kdi = (List<KData>)nti.ObjTag;
                    if (kdi.Count == _urldayscount + 1)
                        kdi = kdi.Skip(1).ToList();
                    nti.Valid = true;
                    if (kdi.Count == 0 || kdi.Count == 1 )
                    {
                        nti.Valid = false;
                        continue;
                    }
                    nti.SetLatestKData(kdi[kdi.Count - 1]);
                    nti.SetOldestKData( kdi[0]);
                    nti.ObjTag = kdi.Skip(1).Take(_NeedDownLoadDays-1 < kdi.Count - 2 ? _NeedDownLoadDays-1 : kdi.Count - 2 ).ToList();                  
                }
            }
            return Msg;
        }
        private bool NeedDownLoadAllData(ref int mergersday)
        {
            if (!LoadLocalData()) //下载完整数据
                return true;
            if (_localkdtags.Tag[0] == null || _localkdtags.Tag[0].kd == null || _localkdtags.Tag[0].kd.Count == 0)
                return true;

            int LocalLatestDate = _localkdtags.Tag[0].Latestkd.date;
            int LocalListDateCount = _localkdtags.Tag[0].kd.Count + 1;
            long LatestDateMin = Convert.ToInt64(_localkdtags.StoreDate.ToString("yyyyMMddHHmmss"));
            int cnt = _netdate.GetAfterDays(LocalLatestDate,LatestDateMin);
            if (LocalListDateCount+1 != _urldayscount)
                return true;
            mergersday = cnt;
            if (mergersday > 98) //10个周期没有更新，则全部重新更新
                return true;
            if ( cnt<3  && _localkdtags.StoreDate >= _netdate.LatestDateTime )
                mergersday = -1;
            return false; //部分下载
        }
        private void DownLoadNetDataAndCheckSave(int Dayscount)
        {
            this._NeedDownLoadDays = Dayscount;
            DownDataFromNet(ThreadShowMsg);
            //UpdateFin
            List<Stock> list = GetValidStockList();
            if(list.Count>0)
            {
                OutNeedReDownLoadStockNames(list);
                ThreadShowMsg("需要再次更新股票数目为" + list.Count);
                _NeedDownLoadDays = _urldayscount;
                DownDataFromNet(ThreadShowMsg, list);
            }
        }
        private void OutNeedReDownLoadStockNames(List<Stock> list)
        {
            string txt = string.Join("\r\n", list.Select(r1 => r1.ID + "\t" + r1.Name + "\t" + r1.PYCode).ToList());
            MFile.AppendAllText(this.stocks.Pathconfig.NowWorkPath() + _kdatatype + "_含有除权数据，需要重新下载.log", "含有除权的股票名称\r\n" + txt);
        }
        private void DownDataFromNet(ShowDeleGate ThreadShowMsg, List<Stock> list=null) //等待线程结束 才返回
        {
            if (!_isrunning)
            {
                _isrunning = true;
                _tnkd = new NetKData(Jhconfig,_kdatatype);
                _tnkd.ThreadShowMsg = ThreadShowMsg;
                _tnkd.CompleteRun = CompleteRun;
                _tnkd.DaysCount = _NeedDownLoadDays;
                if (list != null)
                {
                    _tnkd.SetCustomMode(true);
                    _tnkd.DaysCount = _urldayscount;
                    _tnkd.CustomDealStocks = list;
                    _tnkd.CompleteRun = CompleteRunMergerList;
                }
                System.Threading.Thread nonParameterThread =
                    new Thread(new ThreadStart(_tnkd.GetNetKData));
                nonParameterThread.Start();
            }
            while (_isrunning)
                Thread.Sleep(100);
        }        
        private List<Stock> GetValidStockList()
        {
            return _localkdtags.Tag.Where( r1 => r1!=null  && r1.kd!=null && !r1.Valid)
                .Select( r2 =>   stocks.StockByIndex( r2.index) ).ToList();
        }
        public List<Stock> GetFhsgStocks(int FDLatestDate)
        {
            return LocalkdTags.Tag.Where(r1 => r1 != null && r1.kd != null && r1.Valid)
                .Where( r3 => 
                {
                    return r3.kd.Exists(r4 => r4.date > FDLatestDate && r4.reservation > 0)
                        || r3.Latestkd.date > FDLatestDate && r3.Latestkd.reservation > 0;
                })
                .Select( r2 =>   stocks.StockByIndex( r2.index) ).ToList();
        }
        public void CompleteRunMergerList()
        {//数据的处理合并
            string Msg = ExCludeExchangeDayData();
            {
                StringBuilder sb = new StringBuilder(); // 合并   
                foreach( NetObjectTagstock nt in  _tnkd._NetObjTags.Tag){
                    if(nt == null ||nt.value!=1 || nt.ObjTag == null) continue;
                    LocalKdTagstock lt = _localkdtags.Tag[nt.index];
                    lt.kd = (List<KData>)nt.ObjTag;
                    lt.Latestkd = nt.LatestKData;
                    lt.Valid = true;
                }                
            }// 理论上不存在
            _localkdtags.StoreFinDate = _localkdtags.StoreIntDate();
            _localkdtags.Save(locatepricedatafile);
            _isrunning = false;
        }
        public bool HasLocalKdata(int sID)
        {
            if (LocalkdTags == null)
                return false;
            if (_localkdtags.Tag != null && _localkdtags.Tag[sID] != null)
                return true;
            return false;
        }
        public List<KData> GetLocalKD(int sID)
        {
            if (HasLocalKdata(sID))
                return _localkdtags.Tag[sID].Akd;
            return new List<KData>();
        }
        public LocalKdTagstocks LocalkdTags
        {
            get
            {
                if (_localkdtags == null)
                    LoadLocalData();
                return _localkdtags;
            }
        }
        public int GetDatebydaycount(int dayscount)
        {
            return _netdate.GetDateByAfterDays(dayscount);
        }
        public bool Contains(int begindate)
        {
            return _netdate.Contains(begindate);
        }
        public bool Inline { get { _netdate.TestFresh(); return _netdate.Inline; } }

        private bool LoadLocalData()
        {
            Msg = "";
            if (_localkdtags != null)// && _jscfg.globalconfig.Tempconfig.
            {
                Msg = "本地数据已加载过，无需再次加载";
                return true;
            }
            if (!File.Exists(locatepricedatafile))
            {
                Msg = "本地记录不存在";
                return false;
            }
            string txt = File.ReadAllText(locatepricedatafile);
            _localkdtags = JsonConvert.DeserializeObject<LocalKdTagstocks>(txt);
            if (_localkdtags.Tag.Count() != 2000)
            {
                Msg = "本地数据记录有误，或者格式不对，请从新下载生成";
                return false;
            }
            return true; //应改为 false
        }
        private Boolean IsEqual(KData A, KData B)
        {
            return A.date == B.date && A.open == B.open && A.high == B.high && A.low == B.low && A.close == B.close
                 && A.vol == B.vol;
        }
        
        private NetKData _tnkd;
        private NetDate _netdate;
        private int _NeedDownLoadDays; // 临时记录长度
        private bool _isrunning;

        public ShowDeleGate ThreadShowMsg;
        public ActionDeleGate ActionMsg;
        public NetDate Netdate { get { return _netdate; } }
        private Stocks stocks{get {return Jhconfig.Stocks;}}
        private Stock ssestock { get {return stocks.Sse;}}
        public string Msg{get;set;}
        private string locatepricedatafile { get { return Jhconfig.Jsconfig.pathconfig.WorkPath + "" + _kdatatype + "_ExpPrice.dat"; } }
        public  string GetKDataChineseType()
        {
            switch (KdataType.ToLower())
            {
                case "dayly": return "日线";
                case "weekly": return "周线";
                case "monthly": return "年线";
                case "m60ly": return "时线";
                case "m30ly": return "半时线";
                case "m15ly": return "15min线";
                case "m5ly": return "5min线";
                case "m1ly": return "1min线";
            }
            return "日线";
        }
        private JHConfig Jhconfig;
        private string _kdatatype;
        private int _urldayscount;
        private LocalKdTagstocks _localkdtags;
        public string KdataType { get { return _kdatatype; } }


    }
    public class NetDate
    {
        public NetDate(int DaysCount, string urltype = "dayly")
        {
            this._urldayscount = DaysCount;            
            _urlt = new ThreadUpdateUrlTemplate(urltype).UrlTemplate;
            _daytpye = urltype.Replace("ly", "");
            Init();
        }
        public void TestFresh()
        {
            if (_netszdate == null)
                Refresh();
        }
        public void Refresh()
        {
            string txt = "";
            try
            {
                _Msg = "";
                Init();
                txt = DownLoadNetSzDate(txt);
                if (LatestDateMin / 10000 % 100 != 15)
                    ExChanging = true;
                Inline = true;
                LatestDate = _netszdate.Max();
                _netdayscount = _netszdate.Count;
                if (_netdayscount == _urldayscount + 1)
                    _netszdate = _netszdate.Skip(1).ToList();
                //LatestDate = _netszdate[_urldayscount - 1];
            }
            catch (Exception emsg)
            {
                _Msg = emsg.Message;
                Init();
            }
            try
            {
                string dateString = LatestDateMin.ToString();
                DateTime dt = DateTime.ParseExact(dateString, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
                LatestDateTime = dt;
            }
            catch
            {
                string dateString = LatestDate.ToString();
                DateTime dt = DateTime.ParseExact(dateString, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
                LatestDateTime = dt;
            }

        }
        public int GetAfterDays(int LocalLatestDate,long LocLatestMin)
        {
            int cnt = 0;
            if (_netszdate == null)
                Refresh();
            if (_netszdate != null)
            {
                cnt = _netszdate.Count(r => r > LocalLatestDate) +2;
                if ( _urldayscount+1 == _netdayscount)
                    cnt--;
                //if()
                //    cnt --;
                return cnt;
            }
            return -1;
        }
        public int GetDateByAfterDays(int dayscount)
        {
            if (dayscount < 0 || dayscount > _netszdate.Count)
                return -1;
            return _netszdate[_netszdate.Count -  dayscount];
        }

        private void Init()
        {
            Inline = false;
            ExChanging = false;
            _netdayscount =  0;
            LatestDate = 0;
            _netszdate = null;
            LatestDateTime = DateTime.Now;
        }
        private string DownLoadNetSzDate(string txt)
        {
            string url = _urlt.Replace("[stockcode]", "sh000001").Replace("[dayscount]", _urldayscount.ToString());
            txt = CWeb.GetWebClient(url);
            if (txt.Contains("={"))
                txt = txt.Substring(txt.IndexOf("=") + 1);
            txt = txt.Replace(_daytpye, "day");
            QQStocks qs = JsonConvert.DeserializeObject<QQStocks>(txt);
            _netszdate = qs.data["sh000001"].day.Select(
                r =>
                {
                    //string str1 = r[0].ToString().Replace("-", "");
                    long date = Convert.ToInt64(r[0].ToString().Replace("-", ""));
                    if (date > Int32.MaxValue)
                    {
                        long year = date / 100000000 - 2017; //小时数据不超过 2017年
                        date = year * 100000000 + (int)(date % 100000000);
                    }
                    return (int)date;
                }).ToList();

            LatestDateMin = 0;
            string strqt = qs.data["sh000001"].qt.ToString();
            int bpos =  strqt.IndexOf("sh000001");
            if(bpos!=-1)
            {
                int epos = strqt.IndexOf( ']' , bpos + 10);
                if (epos != -1)
                {
                    string s = strqt.Substring(bpos + 10, epos - bpos);
                    string[] items = s.Split(',');
                    if (items.Length >56 )
                    {
                        LatestDateMin = Convert.ToInt64(items[30].Trim().Replace("\"",""));
                    }
                }
            }              
            return txt;
        }
        public bool Contains(int begindate)
        {
            TestFresh();
            return _netszdate.Contains(begindate);
        }
        public int LatestDate { get; set; }
        public long LatestDateMin { get; set; }     
        public DateTime LatestDateTime;      
        public bool Inline { get; set; }
        private bool ExChanging{ get; set; }  

        private List<int> _netszdate;
        private int _urldayscount;
        private int _netdayscount;
        private string _daytpye;
        private string _urlt;
        private string _Msg;

    }
}
